home *** CD-ROM | disk | FTP | other *** search
/ The Atari Compendium / The Atari Compendium (Toad Computers) (1994).iso / files / prgtools / mint / shells / tcshsrc.zoo / tcsh / tw.parse.c < prev    next >
Encoding:
C/C++ Source or Header  |  1991-11-21  |  41.1 KB  |  1,604 lines

  1. /* $Header: /home/hyperion/mu/christos/src/sys/tcsh-6.00/RCS/tw.parse.c,v 3.8 1991/07/23 23:20:08 christos Exp $ */
  2. /*
  3.  * tw.parse.c: Everyone has taken a shot in this futile effort to
  4.  *           lexically analyze a csh line... Well we cannot good
  5.  *           a job as good as sh.lex.c; but we try. Amazing that
  6.  *           it works considering how many hands have touched this code
  7.  */
  8. /*-
  9.  * Copyright (c) 1980, 1991 The Regents of the University of California.
  10.  * All rights reserved.
  11.  *
  12.  * Redistribution and use in source and binary forms, with or without
  13.  * modification, are permitted provided that the following conditions
  14.  * are met:
  15.  * 1. Redistributions of source code must retain the above copyright
  16.  *    notice, this list of conditions and the following disclaimer.
  17.  * 2. Redistributions in binary form must reproduce the above copyright
  18.  *    notice, this list of conditions and the following disclaimer in the
  19.  *    documentation and/or other materials provided with the distribution.
  20.  * 3. All advertising materials mentioning features or use of this software
  21.  *    must display the following acknowledgement:
  22.  *    This product includes software developed by the University of
  23.  *    California, Berkeley and its contributors.
  24.  * 4. Neither the name of the University nor the names of its contributors
  25.  *    may be used to endorse or promote products derived from this software
  26.  *    without specific prior written permission.
  27.  *
  28.  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
  29.  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
  30.  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
  31.  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
  32.  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
  33.  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
  34.  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
  35.  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
  36.  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
  37.  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
  38.  * SUCH DAMAGE.
  39.  */
  40. #include "config.h"
  41. RCSID("$Id: tw.parse.c,v 3.8 1991/07/23 23:20:08 christos Exp $")
  42.  
  43. #include "sh.h"
  44. #include "tw.h"
  45. #include "ed.h"
  46. #include "tc.h"
  47.  
  48. #ifdef __MINT__
  49. extern Char *Lastslash();
  50. #endif
  51.  
  52. /* #define TENEDEBUG */
  53.  
  54. /* true if the path has relative elements */
  55. static bool relatives_in_path;
  56.  
  57. static int maxitems = 0;
  58. Char  **command_list = (Char **) NULL;    /* the pre-digested list of commands
  59.                      * for speed and general usefullness */
  60. int     numcommands = 0;
  61. int     have_sorted = 0;
  62.  
  63. /* Set to TRUE if recexact is set and an exact match is found
  64.  * along with other, longer, matches.
  65.  */
  66. int non_unique_match = FALSE;
  67.  
  68. #ifdef notdef
  69. int     dirctr = 0;        /* -1 0 1 2 ... */
  70.  
  71. #endif
  72. Char    dirflag[5];        /* ' nn\0' - dir #s -  . 1 2 ... */
  73.  
  74. static bool SearchNoDirErr = 0;    /* t_search returns -2 if dir is unreadable */
  75.  
  76. /* do the expand or list on the command line -- SHOULD BE REPLACED */
  77.  
  78. extern Char NeedsRedraw;    /* from ed.h */
  79. extern int TermH;        /* from the editor routines */
  80. extern int lbuffed;        /* from sh.print.c */
  81.  
  82. static    void     free_items        __P((Char **, int));
  83. static    void     extract_dir_and_name    __P((Char *, Char *, Char *));
  84. static    Char    *quote_meta        __P((Char *, bool));
  85. static    Char    *getentry        __P((DIR *, int));
  86. static    Char    *dollar            __P((Char *, Char *));
  87. static    Char    *tilde            __P((Char *, Char *));
  88. static    Char     filetype        __P((Char *, Char *));
  89. static    int     t_glob            __P((Char ***));
  90. static    int     is_prefix        __P((Char *, Char *));
  91. static    int     is_suffix        __P((Char *, Char *));
  92. static    int     recognize        __P((Char *, Char *, int, int));
  93. static    int     ignored        __P((Char *));
  94. static    void     tw_get_comm_list    __P((void));
  95. static    int     isadirectory        __P((Char *, Char *));
  96.  
  97. /*
  98.  * If we find a set command, then we break a=b to a= and word becomes
  99.  * b else, we don't break a=b.
  100.  */
  101. #define isaset(c, w) ((w)[-1] == '=' && \
  102.               ((c)[0] == 's' && (c)[1] == 'e' && (c)[2] == 't' && \
  103.                ((c[3] == ' ' || (c)[3] == '\t'))))
  104. /*
  105.  * Return value for tenematch():
  106.  *  > 1:    No. of items found
  107.  *  = 1:    Exactly one match / spelling corrected
  108.  *  = 0:    No match / spelling was correct
  109.  *  < 0:    Error (incl spelling correction impossible)
  110.  */
  111. int
  112. tenematch(inputline, inputline_size, num_read, command)
  113.     Char   *inputline;        /* match string prefix */
  114.     int     inputline_size;    /* max size of string */
  115.     int     num_read;        /* # actually in inputline */
  116.     COMMAND command;        /* LIST or RECOGNIZE or PRINT_HELP */
  117.  
  118. {
  119.     Char    word[FILSIZ + 1];
  120.     register Char *str_end, *word_start, *cmd_start, *wp;
  121.     Char   *cmd_st;
  122.     int     space_left;
  123.     int     is_a_cmd;        /* UNIX command rather than filename */
  124.     int     search_ret;        /* what search returned for debugging */
  125.     int     in_single, in_double;    /* In single or in_double quotes */
  126.  
  127.     str_end = &inputline[num_read];
  128.  
  129.     /*
  130.      * space backward looking for the beginning of this command
  131.      */
  132.     for (cmd_st = str_end; cmd_st > inputline; --cmd_st)
  133.     if (iscmdmeta(cmd_st[-1])
  134.         && ((cmd_st - 1 == inputline) || (cmd_st[-2] != escchar)))
  135.         break;
  136.     /* step forward over leading spaces */
  137. #ifdef __MINT__
  138.     while (*cmd_st && (*cmd_st == ' ' || *cmd_st == '\t' || *cmd_st == '\r'))
  139. #else
  140.     while (*cmd_st != '\0' && (*cmd_st == ' ' || *cmd_st == '\t'))
  141. #endif
  142.     cmd_st++;
  143.  
  144.     /*
  145.      * Find LAST occurence of a delimiter in the inputline. The word start is
  146.      * one character past it.
  147.      */
  148.     for (word_start = str_end; word_start > inputline; --word_start) {
  149.     if ((ismeta(word_start[-1]) || isaset(cmd_st, word_start)) &&
  150.         (word_start[-1] != '#') && (word_start[-1] != '$') &&
  151.         ((word_start - 1 == inputline) || (word_start[-2] != escchar)))
  152.         break;
  153.     }
  154.  
  155.  
  156.  
  157. #ifdef    masscomp
  158.     /*
  159.      * Avoid a nasty message from the RTU 4.1A & RTU 5.0 compiler concerning
  160.      * the "overuse of registers". According to the compiler release notes,
  161.      * incorrect code may be produced unless the offending expression is
  162.      * rewritten. Therefore, we can't just ignore it, DAS DEC-90.
  163.      */
  164.     space_left = inputline_size;
  165.     space_left -= word_start - inputline + 1;
  166. #else
  167.     space_left = inputline_size - (word_start - inputline) - 1;
  168. #endif
  169.  
  170.     is_a_cmd = starting_a_command(word_start, inputline);
  171. #ifdef TENEDEBUG
  172.     xprintf("starting_a_command %d\n", is_a_cmd);
  173. #endif
  174.  
  175.     /*
  176.      * Quote args
  177.      */
  178.     in_double = 0;
  179.     in_single = 0;
  180.     for (cmd_start = word_start, wp = word; cmd_start < str_end && wp <= word + FILSIZ; cmd_start++)
  181.     switch (*cmd_start) {
  182.     case '\'':
  183.         if (!in_double) {
  184.         if (in_single)
  185.             in_single = 0;
  186.         else
  187.             in_single = QUOTE;
  188.         /*
  189.          * Move the word_start further, cause the quotes so far have no
  190.          * effect.
  191.          */
  192.         if (cmd_start == word_start)
  193.             word_start++;
  194.         }
  195.         else
  196.         *wp++ = *cmd_start | QUOTE;
  197.         break;
  198.     case '"':
  199.         if (!in_single) {
  200.         if (in_double)
  201.             in_double = 0;
  202.         else
  203.             in_double = QUOTE;
  204.         /*
  205.          * Move the word_start further, cause the quotes so far have no
  206.          * effect.
  207.          */
  208.         if (cmd_start == word_start)
  209.             word_start++;
  210.         }
  211.         else
  212.         *wp++ = *cmd_start | QUOTE;
  213.         break;
  214.     case '/':
  215.         /*
  216.          * This is so that the recognize stuff works easily
  217.          */
  218.         *wp++ = *cmd_start;
  219.         break;
  220. #ifndef ALTESC
  221.     case '\\':
  222.         if (in_single || in_double)
  223.         *wp++ = *cmd_start | QUOTE;
  224.         else
  225.         *wp++ = *++cmd_start | QUOTE;
  226.         break;
  227. #endif
  228.     default:
  229. #ifdef ALTESC
  230.         if (*cmd_start == escchar) {
  231.         if (in_single || in_double)
  232.             *wp++ = *cmd_start | QUOTE;
  233.         else
  234.             *wp++ = *++cmd_start | QUOTE;
  235.         } else
  236. # ifdef __MINT__
  237.         if (*cmd_start == '\\')
  238.             *wp++ = *cmd_start;
  239.         else
  240. # endif
  241. #endif
  242.         *wp++ = *cmd_start | in_single;
  243.         break;
  244.     }
  245.     if (wp > word + FILSIZ)
  246.     return (-1);
  247.     *wp = '\0';
  248.  
  249.  
  250. #ifdef TENEDEBUG
  251.     xprintf("\ncmd_st:%s:\n", short2str(cmd_st));
  252.     xprintf("word:%s:\n", short2str(word));
  253.     xprintf("word:");
  254.     for (wp = word; *wp; wp++)
  255.     xprintf("%c", *wp & QUOTE ? '-' : ' ');
  256.     xprintf(":\n");
  257. #endif
  258.     switch ((int) command) {
  259.     Char    buffer[FILSIZ + 1], *bptr;
  260.     Char   *slshp;
  261.     Char   *items[2], **ptr;
  262.     int     i, count;
  263.  
  264.     case RECOGNIZE:
  265.     if (adrof(STRautocorrect)) {
  266. #ifdef __MINT__
  267.         if ((slshp = Lastslash(word)) != NULL && slshp[1] != '\0') {
  268. #else
  269.         if ((slshp = Strrchr(word, '/')) != NULL && slshp[1] != '\0') {
  270. #endif
  271.         SearchNoDirErr = 1;
  272.         for (bptr = word; bptr < slshp; bptr++) {
  273.             /*
  274.              * do not try to correct spelling of words containing
  275.              * globbing characters
  276.              */
  277.             if (isglob(*bptr)) {
  278.             SearchNoDirErr = 0;
  279.             break;
  280.             }
  281.         }
  282.         }
  283.     }
  284.     else
  285.         slshp = STRNULL;
  286.     search_ret = t_search(word, wp, command, space_left, is_a_cmd, 1);
  287.     SearchNoDirErr = 0;
  288.  
  289.     if (search_ret == -2) {
  290.         Char    rword[FILSIZ + 1];
  291.  
  292.         (void) Strcpy(rword, slshp);
  293.         if (slshp != STRNULL)
  294.         *slshp = '\0';
  295.         if ((search_ret = spell_me(word, sizeof(word), is_a_cmd)) == 1) {
  296.         DeleteBack(str_end - word_start);/* get rid of old word */
  297.         (void) Strcat(word, rword);
  298.         if (InsertStr(word) < 0)    /* insert newly spelled word */
  299.             return -1;    /* error inserting */
  300.         wp = word + Strlen(word);
  301.         search_ret = t_search(word, wp, command, space_left,
  302.                       is_a_cmd, 1);
  303.         }
  304.     }
  305.  
  306.     /*
  307.      * Change by Christos Zoulas: if the name has metachars in it, quote
  308.      * the metachars, but only if we are outside quotes.
  309.      */
  310.     if (*wp && InsertStr((in_single || in_double) ?
  311.                  wp : quote_meta(wp,
  312.                          (bool) is_set(STRaddsuffix))) < 0)
  313.         /* put it in the input buffer */
  314.         return -1;        /* error inserting */
  315.     return search_ret;
  316.  
  317.     case SPELL:
  318.     for (bptr = word_start; bptr < str_end; bptr++) {
  319.         /*
  320.          * do not try to correct spelling of words containing globbing
  321.          * characters
  322.          */
  323.         if (isglob(*bptr))
  324.         return 0;
  325.     }
  326.     if ((search_ret = spell_me(word, sizeof(word), is_a_cmd)) == 1) {
  327.         DeleteBack(str_end - word_start);    /* get rid of old word */
  328.         if (InsertStr(word) < 0)    /* insert newly spelled word */
  329.         return -1;    /* error inserting */
  330.     }
  331.     return search_ret;
  332.  
  333.     case PRINT_HELP:
  334.     do_help(cmd_st);
  335.     return 1;
  336.  
  337.     case GLOB:
  338.     case GLOB_EXPAND:
  339.     (void) Strncpy(buffer, word, FILSIZ + 1);
  340.     items[0] = buffer;
  341.     items[1] = NULL;
  342.     ptr = items;
  343.     if (is_a_cmd) {
  344.         xprintf("Sorry no globbing for commands yet..\n");
  345.         return 0;
  346.     }
  347.     if ((count = t_glob(&ptr)) > 0) {
  348.         if (command == GLOB)
  349.         print_by_column(STRNULL, ptr, count, is_a_cmd);
  350.         else {
  351.         DeleteBack(str_end - word_start);/* get rid of old word */
  352.         for (i = 0; i < count; i++)
  353.             if (ptr[i] && *ptr[i]) {
  354.             if (InsertStr((in_single || in_double) ?
  355.                       ptr[i] : quote_meta(ptr[i], 0)) < 0 ||
  356.                 InsertStr(STRspace) < 0) {
  357.                 blkfree(ptr);
  358.                 return (-1);
  359.             }
  360.             }
  361.         }
  362.         blkfree(ptr);
  363.     }
  364.     return count;
  365.  
  366.     case VARS_EXPAND:
  367.     if (dollar(buffer, word)) {
  368.         DeleteBack(str_end - word_start);
  369.         if (InsertStr((in_single || in_double) ?
  370.               buffer : quote_meta(buffer, 0)) < 0)
  371.         return (-1);
  372.         return (1);
  373.     }
  374.     return (0);
  375.  
  376.     case LIST:
  377.     search_ret = t_search(word, wp, command, space_left, is_a_cmd, 1);
  378.     return search_ret;
  379.  
  380.     default:
  381.     xprintf("tcsh: Internal match error.\n");
  382.     return 1;
  383.  
  384.     }
  385. }
  386.  
  387.  
  388.  
  389.  
  390. static int
  391. t_glob(v)
  392.     register Char ***v;
  393. {
  394.     jmp_buf osetexit;
  395.  
  396.     if (**v == 0)
  397.     return (0);
  398.     gflag = 0, tglob(*v);
  399.     if (gflag) {
  400.     getexit(osetexit);    /* make sure to come back here */
  401.     if (setexit() == 0)
  402.         *v = globall(*v);
  403.     resexit(osetexit);
  404.     gargv = 0;
  405.     if (haderr) {
  406.         haderr = 0;
  407.         NeedsRedraw = 1;
  408.         return (-1);
  409.     }
  410.     if (*v == 0)
  411.         return (0);
  412.     }
  413.     else
  414.     return (0);
  415.  
  416.     return (gargc);
  417. }
  418.  
  419.  
  420. /*
  421.  * quote (\) the meta-characters in a word
  422.  * except trailing space if trail_space is set
  423.  * return pointer to quoted word in static storage
  424.  */
  425. static Char *
  426. quote_meta(word, trail_space)
  427.     Char   *word;
  428.     bool    trail_space;
  429. {
  430.     static Char buffer[2 * FILSIZ + 1], *bptr, *wptr;
  431.  
  432.     for (bptr = buffer, wptr = word; *wptr != '\0';) {
  433.     if ((cmap(*wptr, _META | _DOL | _Q | _ESC | _GLOB) || *wptr == HIST ||
  434.          *wptr == HISTSUB) &&
  435.         (*wptr != ' ' || !trail_space || *(wptr + 1) != '\0'))
  436.         *bptr++ = escchar;
  437.     *bptr++ = *wptr++;
  438.     }
  439.     *bptr = '\0';
  440.     return (buffer);
  441. }
  442.  
  443.  
  444. /*
  445.  * return true if check items initial chars in template
  446.  * This differs from PWB imatch in that if check is null
  447.  * it items anything
  448.  */
  449.  
  450. static int
  451. is_prefix(check, template)
  452.     register Char *check, *template;
  453. {
  454.     for (; *check; check++, template++)
  455.     if ((*check & TRIM) != (*template & TRIM))
  456.         return (FALSE);
  457.     return (TRUE);
  458. }
  459.  
  460. /*
  461.  *  Return true if the chars in template appear at the
  462.  *  end of check, I.e., are it's suffix.
  463.  */
  464. static int
  465. is_suffix(check, template)
  466.     register Char *check, *template;
  467. {
  468.     register Char *t, *c;
  469.  
  470.     for (t = template; *t++;);
  471.     for (c = check; *c++;);
  472.     for (;;) {
  473.     if (t == template)
  474.         return 1;
  475.     --t;
  476.     --c;
  477.     if (c == check || (*t & TRIM) != (*c & TRIM))
  478.         return 0;
  479.     }
  480. }
  481.  
  482. static int
  483. ignored(entry)
  484.     register Char *entry;
  485. {
  486.     struct varent *vp;
  487.     register Char **cp;
  488.  
  489.     if ((vp = adrof(STRfignore)) == NULL || (cp = vp->vec) == NULL)
  490.     return (FALSE);
  491.     for (; *cp != NULL; cp++)
  492.     if (is_suffix(entry, *cp))
  493.         return (TRUE);
  494.     return (FALSE);
  495. }
  496.  
  497. /* return true if the command starting at wordstart is a command */
  498.  
  499. #define EVEN(x) (((x) & 1) != 1)
  500.  
  501. int
  502. starting_a_command(wordstart, inputline)
  503.     register Char *wordstart, *inputline;
  504. {
  505.     register Char *ptr, *ncmdstart;
  506.     int     count;
  507.     static  Char
  508.             cmdstart[] = {'`', ';', '&', '(', '|', '\0'},
  509.             cmdalive[] = {' ', '\t', '\'', '"', '<', '>', '\0'};
  510.  
  511.     /*
  512.      * Find if the number of backquotes is odd or even.
  513.      */
  514.     for (ptr = wordstart, count = 0;
  515.      ptr >= inputline;
  516.      count += (*ptr-- == '`'));
  517.     /*
  518.      * if the number of backquotes is even don't include the backquote char in
  519.      * the list of command starting delimiters [if it is zero, then it does not
  520.      * matter]
  521.      */
  522.     ncmdstart = cmdstart + EVEN(count);
  523.  
  524.     /*
  525.      * look for the characters previous to this word if we find a command
  526.      * starting delimiter we break. if we find whitespace and another previous
  527.      * word then we are not a command
  528.      * 
  529.      * count is our state machine: 0 looking for anything 1 found white-space
  530.      * looking for non-ws
  531.      */
  532.     for (count = 0; wordstart >= inputline; wordstart--) {
  533.     if (*wordstart == '\0')
  534.         continue;
  535.     if (Strchr(ncmdstart, *wordstart))
  536.         break;
  537.     /*
  538.      * found white space
  539.      */
  540.     if (ptr = Strchr(cmdalive, *wordstart))
  541.         count = 1;
  542.     if (count == 1 && !ptr)
  543.         return (FALSE);
  544.     }
  545.  
  546.     if (wordstart > inputline)
  547.     switch (*wordstart) {
  548.     case '&':        /* Look for >& */
  549.         while (wordstart > inputline &&
  550.            (*--wordstart == ' ' || *wordstart == '\t'));
  551.         if (*wordstart == '>')
  552.         return (FALSE);
  553.         break;
  554.     case '(':        /* check for foreach, if etc. */
  555.         while (wordstart > inputline &&
  556.            (*--wordstart == ' ' || *wordstart == '\t'));
  557.         if (!iscmdmeta(*wordstart) &&
  558.         (*wordstart != ' ' && *wordstart != '\t'))
  559.         return (FALSE);
  560.         break;
  561.     default:
  562.         break;
  563.     }
  564.     return (TRUE);
  565. }
  566.  
  567.  
  568.  
  569. /*
  570.  * Object: extend what user typed up to an ambiguity.
  571.  * Algorithm:
  572.  * On first match, copy full entry (assume it'll be the only match)
  573.  * On subsequent matches, shorten extended_name to the first
  574.  * character mismatch between extended_name and entry.
  575.  * If we shorten it back to the prefix length, stop searching.
  576.  */
  577. static int
  578. recognize(extended_name, entry, name_length, numitems)
  579.     Char   *extended_name, *entry;
  580.     int     name_length, numitems;
  581. {
  582.     if (numitems == 1)        /* 1st match */
  583.     copyn(extended_name, entry, MAXNAMLEN);
  584.     else {            /* 2nd and subsequent matches */
  585.     register Char *x, *ent;
  586.     register int len = 0;
  587.  
  588.     for (x = extended_name, ent = entry;
  589.          *x && (*x & TRIM) == (*ent & TRIM); x++, len++, ent++);
  590.     *x = '\0';        /* Shorten at 1st char diff */
  591.     if (len == name_length)    /* Ambiguous to prefix? */
  592.         return (-1);    /* So stop now and save time */
  593.     }
  594.     return (0);
  595. }
  596.  
  597.  
  598.  
  599. /*
  600.  * Perform a RECOGNIZE or LIST command on string "word".
  601.  *
  602.  * Return value:
  603.  *  >= 0:   SPELL command: "distance" (see spdist())
  604.  *          other:         No. of items found
  605.  *  < 0:    Error (message or beep is output)
  606.  */
  607.  
  608. /*ARGSUSED*/
  609. int
  610. t_search(word, wp, command, max_word_length, looking_for_command, list_max)
  611.     Char   *word, *wp;        /* original end-of-word */
  612.     COMMAND command;
  613.     int     max_word_length, looking_for_command, list_max;
  614. {
  615.     register ignoring = 1, nignored = 0;
  616.     register name_length,    /* Length of prefix (file name) */
  617.             looking_for_lognames;    /* True if looking for login names */
  618.     int     showpathn;        /* True if we want path number */
  619.     Char    tilded_dir[FILSIZ + 1],    /* dir after ~ expansion */
  620.             dollar_dir[FILSIZ + 1],    /* dir after $ expansion */
  621.             dir[FILSIZ + 1],    /* /x/y/z/ part in /x/y/z/f */
  622.             name[MAXNAMLEN + 1],/* f part in /d/d/d/f */
  623.             extended_name[MAXNAMLEN + 1],    /* the recognized (extended)
  624.                          * name */
  625.            *entry = NULL,    /* single directory entry or logname */
  626.            *target;        /* Target to expand/correct/list */
  627.     int     next_command = 0;    /* the next command to take out of */
  628.  
  629.     /* the list of commands */
  630.     int     looking_for_shellvar,    /* true if looking for $foo */
  631.             looking_for_file;    /* true if looking for a file name */
  632.     Char  **pathv;        /* pointer to PATH elements */
  633.     struct varent *v_ptr = NULL;/* current shell variable position */
  634.     Char  **env_ptr = NULL;    /* current env. variable position */
  635.  
  636.     int     d = 4, nd;        /* distance and new distance to command for
  637.                  * SPELL */
  638.     int     exec_check = 0, dir_ok = 0;    /* need to check
  639.                      * executability/directory */
  640.  
  641.     static  Char        /* For unset path         */
  642.     *       pv[2] = {STRNULL, NULL};
  643.     static  DIR
  644.     *       dir_fd = NULL;
  645.     static  Char
  646.     **      items = NULL;    /* file names when doing a LIST */
  647.  
  648.     /*
  649.      * bugfix by Marty Grossman (grossman@CC5.BBN.COM): directory listing can
  650.      * dump core when interrupted
  651.      */
  652.     static int numitems;
  653.  
  654.     pathv = (v_ptr = adrof(STRPATH)) == NULL ? pv : v_ptr->vec;
  655.  
  656.     if (items != NULL)
  657.     FREE_ITEMS(items, numitems);
  658.     numitems = 0;
  659.     if (dir_fd != NULL)
  660.     FREE_DIR(dir_fd);
  661.  
  662.     non_unique_match = FALSE;    /* See the recexact code below */
  663.  
  664.     extract_dir_and_name(word, dir, name);
  665. #ifdef __MINT__
  666.     looking_for_lognames = (*word == '~') && (Lastslash(word) == NULL);
  667.     looking_for_shellvar = (target = Strrchr(name, '$')) &&
  668.     (Lastslash(name) == NULL);
  669.     looking_for_file = (!looking_for_command && !looking_for_lognames &&
  670.             !looking_for_shellvar) || Lastslash(word);
  671. #else
  672.     looking_for_lognames = (*word == '~') && (Strchr(word, '/') == NULL);
  673.     looking_for_shellvar = (target = Strrchr(name, '$')) &&
  674.     (Strchr(name, '/') == NULL);
  675.     looking_for_file = (!looking_for_command && !looking_for_lognames &&
  676.             !looking_for_shellvar) || Strchr(word, '/');
  677. #endif
  678.  
  679.     /* PWP: don't even bother when doing ALL of the commands */
  680.     if (looking_for_command && (*word == '\0')) 
  681.     return (-1);
  682.     tilded_dir[0] = '\0';
  683.     dollar_dir[0] = '\0';
  684.  
  685.     if (looking_for_shellvar) {    /* Looking for a shell var? */
  686.     v_ptr = tw_start_shell_list();
  687.     env_ptr = tw_start_env_list();
  688.     target++;
  689.     }
  690.     else
  691.     target = name;
  692.     if (looking_for_shellvar || looking_for_file) {
  693.     Char   *nd = NULL;
  694.  
  695.     /* Open the directory */
  696.     /* expand ~user/... and variables stuff */
  697.     if ((dollar(dollar_dir, dir) == 0) ||
  698.         (tilde(tilded_dir, dollar_dir) == 0) ||
  699.         !(nd = dnormalize(*tilded_dir ? tilded_dir : STRdot)) ||
  700.         ((dir_fd = opendir(short2str(nd))) == NULL)) {
  701.         xfree((ptr_t) nd);
  702.         if (command == SPELL || SearchNoDirErr)
  703.         return (-2);
  704.         xprintf("\n%s unreadable\n",
  705.             *tilded_dir ? short2str(tilded_dir) :
  706.             (*dollar_dir ? short2str(dollar_dir) : short2str(dir)));
  707.         NeedsRedraw = 1;
  708.         return (-1);
  709.     }
  710.     if (nd) {
  711.         if (*tilded_dir != '\0') {
  712.         Char   *s, *d, *p;
  713. #ifdef __MINT__
  714.         Char slashc = '/';
  715.         /*
  716.          * Copy and append a / if there was one
  717.          */
  718.         for (p = tilded_dir; *p; p++);
  719.         --p;
  720.         if (is_dirsep(*p)) {
  721.             slashc = *p;
  722.             for (p = nd; *p; p++);
  723.             --p;
  724.             if (!is_dirsep(*p))
  725.             p = NULL;
  726.         }
  727.         for (d = tilded_dir, s = nd; *d++ = *s++;);
  728.         if (!p) {
  729.             *d-- = '\0';
  730.             *d = slashc;
  731.         }
  732. #else
  733.         /*
  734.          * Copy and append a / if there was one
  735.          */
  736.         for (p = tilded_dir; *p; p++);
  737.         if (*--p == '/') {
  738.             for (p = nd; *p; p++);
  739.             if (*--p != '/')
  740.             p = NULL;
  741.         }
  742.         for (d = tilded_dir, s = nd; *d++ = *s++;);
  743.         if (!p) {
  744.             *d-- = '\0';
  745.             *d = '/';
  746.         }
  747. #endif
  748.         }
  749.         xfree((ptr_t) nd);
  750.     }
  751.     }
  752.     else if (looking_for_lognames) {    /* Looking for login names? */
  753.     /*
  754.      * Check if the spelling was already correct
  755.      * From: Rob McMahon <cudcv@cu.warwick.ac.uk>
  756.      */
  757.     if (command == SPELL && getpwnam(short2str(&word[1])) != NULL) {
  758. #ifdef YPBUGS
  759.         fix_yp_bugs();
  760. #endif /* YPBUGS */
  761.         return (0);
  762.     }
  763.     copyn(name, &word[1], MAXNAMLEN);    /* name sans ~ */
  764.     (void) setpwent();    /* Open passwd file */
  765.     }
  766.     else if (looking_for_command) {
  767.     if (!numcommands)    /* if we have no list of commands */
  768.         tw_get_comm_list();
  769.     if (!have_sorted) {    /* if we haven't sorted them yet */
  770.         tw_add_builtins();
  771.         tw_add_aliases();
  772.         tw_sort_comms();    /* re-build the command path for twenex.c */
  773.     }
  774.     copyn(target, word, MAXNAMLEN);    /* so it can match things */
  775.     }
  776.     else {
  777.     xprintf("\ntcsh internal error: I don't know what I'm looking for!\n");
  778.     NeedsRedraw = 1;
  779.     return (-1);
  780.     }
  781.  
  782.  
  783. again:
  784.     name_length = Strlen(target);
  785.     showpathn = looking_for_command && is_set(STRlistpathnum);
  786.  
  787.     while (1) {
  788.     if (looking_for_shellvar) {
  789.         if ((entry = tw_next_shell_var(&v_ptr)) == NULL)
  790.         if ((entry = tw_next_env_var(&env_ptr)) == NULL)
  791.             break;
  792.     }
  793.     else if (looking_for_file || looking_for_lognames) {
  794.         if ((entry = getentry(dir_fd, looking_for_lognames)) == NULL) {
  795.         break;
  796.         }
  797.  
  798.         /*
  799.          * Don't match . files on null prefix match
  800.          */
  801.         if (name_length == 0 && entry[0] == '.' &&
  802.         !looking_for_lognames && !is_set(STRshowdots))
  803.         continue;
  804.         if (looking_for_command && !looking_for_lognames) {
  805.         exec_check = 1;
  806.         dir_ok = 1;
  807.         }
  808.     }
  809.     else if (looking_for_command) {
  810. #ifdef  NOTDEF            /* Not possible */
  811.         if (numcommands == 0) {
  812.         dohash(NULL, NULL);
  813.         }
  814. #endif
  815.         /* searching . added by Andreas Luik <luik@isaak.isa.de> */
  816.  
  817.         if ((next_command < numcommands) &&
  818.         (entry = command_list[next_command]) == NULL)
  819.         next_command = numcommands;
  820.         if (next_command >= numcommands) {    /* search relative elems */
  821.         if (!relatives_in_path)
  822.             break;    /* we don't need to do it */
  823.         while ((dir_fd == NULL ||
  824.             (entry = getentry(dir_fd, FALSE)) == NULL) &&
  825.                *pathv) {
  826.             if (dir_fd != NULL)
  827.             FREE_DIR(dir_fd);
  828.             entry = NULL;
  829. #ifdef __MINT__
  830.             while (*pathv && (pathv[0][0] == '/' || pathv[0][0] == '\\'))
  831.             pathv++;
  832. #else
  833.             while (*pathv && pathv[0][0] == '/')
  834.             pathv++;
  835. #endif
  836.             if (*pathv) {
  837.             /*
  838.              * We complete directories only on '.' should that
  839.              * be changed?
  840.              */
  841.             if (pathv[0][0] == '\0' ||
  842.                 (pathv[0][0] == '.' && pathv[0][1] == '\0')) {
  843.                 *tilded_dir = '\0';
  844.                 dir_fd = opendir(".");
  845.                 dir_ok = 1;    
  846.             }
  847.             else {
  848.                 copyn(tilded_dir, *pathv, FILSIZ);
  849.                 catn(tilded_dir, STRslash, FILSIZ);
  850.                 dir_fd = opendir(short2str(*pathv));
  851.                 dir_ok = 0;
  852.             }
  853.             pathv++;
  854.             }
  855.         }
  856.         if (entry == NULL)
  857.             break;    /* end of PATH */
  858.         /*
  859.          * executability check for other than "." should perhaps be
  860.          * conditional on recognize_only_executables?
  861.          */
  862.         exec_check = 1;
  863.         }
  864.         else
  865.         next_command++;
  866.     }
  867.  
  868.     if (command == SPELL) {    /* correct the spelling of the last bit */
  869.         if (name_length == 0) {/* zero-length word can't be misspelled */
  870.         extended_name[0] = '\0';/* (not trying is important for ~) */
  871.         d = 0;
  872.         break;
  873.         }
  874.         nd = spdist(entry, target);    /* test the entry against original */
  875.         if (nd <= d && nd != 4) {
  876.         if (exec_check && !executable(tilded_dir, entry, dir_ok))
  877.             continue;
  878.         (void) Strcpy(extended_name, entry);
  879.         d = nd;
  880.         if (d == 0)    /* if found it exactly */
  881.             break;
  882.         }
  883.         else if (nd == 4) {
  884.         if (spdir(extended_name, tilded_dir, entry, target)) {
  885.             if (exec_check &&
  886.             !executable(tilded_dir, extended_name, dir_ok))
  887.             continue;
  888.             d = 0;
  889.             break;
  890.         }
  891.         }
  892.     }
  893.     else if (command == LIST) {    /* LIST command */
  894.         register int length;
  895.         register long i;
  896.         register Char **ni, **p2;
  897.  
  898.         if (!is_prefix(target, entry))
  899.         continue;
  900.         if (exec_check && !executable(tilded_dir, entry, dir_ok))
  901.         continue;
  902.  
  903.         if (items == NULL || maxitems == 0) {
  904.         items = (Char **) xmalloc((size_t) (sizeof(items[0]) *
  905.                             (ITEMS_START + 1)));
  906.         maxitems = ITEMS_START;
  907.         for (i = 0, p2 = items; i < maxitems; i++)
  908.             *p2++ = NULL;
  909.         }
  910.         else if (numitems >= maxitems) {
  911.         ni = (Char **) xrealloc((ptr_t) items, (size_t)
  912.                 (sizeof(items[0])) * (maxitems + ITEMS_INCR));
  913.         items = ni;
  914.         maxitems += ITEMS_INCR;
  915.         }
  916.  
  917.  
  918.         length = Strlen(entry) + 1;
  919.         if (showpathn)
  920.         length += Strlen(dirflag);
  921.         if (!looking_for_lognames && !looking_for_shellvar)
  922.         length++;
  923.  
  924.         /* safety check */
  925.         items[numitems] = (Char *) xmalloc((size_t)(length * sizeof(Char)));
  926.  
  927.         copyn(items[numitems], entry, MAXNAMLEN);
  928.  
  929.         if (!looking_for_lognames && !looking_for_shellvar
  930.         && !(looking_for_command && !looking_for_file)) {
  931.         Char    typestr[2];
  932.  
  933.         typestr[0] = filetype(tilded_dir, entry);
  934.         typestr[1] = '\0';
  935.         catn(items[numitems], typestr, MAXNAMLEN);
  936.         }
  937.  
  938.         if (showpathn)
  939.         catn(items[numitems], dirflag, MAXNAMLEN);
  940.         numitems++;
  941.     }
  942.     else {            /* RECOGNIZE command */
  943.         if (!is_prefix(target, entry))
  944.         continue;
  945.         if (exec_check && !executable(tilded_dir, entry, dir_ok))
  946.         continue;
  947.  
  948.         if (ignoring && ignored(entry)) {
  949.         nignored++;
  950.         continue;
  951.         }
  952.         if (is_set(STRrecexact)) {
  953.         if (StrQcmp(target, entry) == 0) {    /* EXACT match */
  954.             copyn(extended_name, entry, MAXNAMLEN);
  955.             numitems = 1;    /* fake into expanding */
  956.             non_unique_match = TRUE;
  957.             break;
  958.         }
  959.         }
  960.         if (recognize(extended_name, entry, name_length, ++numitems))
  961.         break;
  962.     }
  963.     }
  964.  
  965.     if (ignoring && numitems == 0 && nignored > 0) {
  966.     ignoring = 0;
  967.     nignored = 0;
  968.     if (looking_for_lognames)
  969.         (void) setpwent();
  970.     else
  971.         rewinddir(dir_fd);
  972.     goto again;
  973.     }
  974.     if (looking_for_lognames) {
  975. #ifdef YPBUGS
  976.     fix_yp_bugs();
  977. #endif                /* YPBUGS */
  978.     (void) endpwent();
  979.     }
  980.     else if (looking_for_file || looking_for_shellvar ||
  981.          (looking_for_command && relatives_in_path)) {
  982.     if (dir_fd != NULL)
  983.         FREE_DIR(dir_fd);
  984.     }
  985.  
  986.     if (command == RECOGNIZE) {
  987.     if (numitems > 0) {
  988.         if (looking_for_lognames)
  989.         copyn(word, STRtilde, 1);
  990.         else if (looking_for_shellvar) {
  991.         Char   *ptr = Strrchr(word, '$');
  992.  
  993.         *++ptr = '\0';    /* Delete after the dollar */
  994.         }
  995.         else if (looking_for_file)
  996.         copyn(word, dir, max_word_length);    /* put back dir part */
  997.         else
  998.         word[0] = '\0';
  999.         catn(word, extended_name, max_word_length);    /* add extended name */
  1000.         if (is_set(STRaddsuffix)) {
  1001.         if (numitems == 1) {
  1002.             if (looking_for_lognames) {    /* add / */
  1003.             catn(word, STRslash, max_word_length);
  1004.             }
  1005.             else if (looking_for_shellvar) {
  1006.             struct varent *vp = adrof(extended_name);
  1007.             Char   *stp;
  1008.  
  1009.             /*
  1010.              * Don't consider array variables or empty variables
  1011.              */
  1012.             if (vp) {
  1013.                 if (!(stp = vp->vec[0]) || vp->vec[0][0] == '\0' ||
  1014.                 vp->vec[1]) {
  1015.                 catn(word, STRspace, max_word_length);
  1016.                 stp = NULL;
  1017.                 }
  1018.                 else
  1019.                 stp = vp->vec[0];
  1020.             }
  1021.             else if ((stp = Getenv(extended_name)) == NULL)
  1022.                 catn(word, STRspace, max_word_length);
  1023.             if (stp != NULL) {
  1024.                 *--target = '\0';
  1025.                 (void) Strcat(tilded_dir, name);
  1026.                 if (isadirectory(tilded_dir, stp))
  1027.                 catn(word, STRslash, max_word_length);
  1028.                 else
  1029.                 catn(word, STRspace, max_word_length);
  1030.             }
  1031.             }
  1032.             else if (looking_for_file || looking_for_command) {
  1033.             if (isadirectory(tilded_dir, extended_name)) {
  1034.                 catn(word, STRslash, max_word_length);
  1035.             }
  1036.             else {
  1037.                 catn(word, STRspace, max_word_length);
  1038.             }
  1039.             }
  1040.         }
  1041.         }
  1042.     }
  1043.     return (numitems);    /* at the end */
  1044.     }
  1045.     else if (command == LIST) {
  1046.     register int max_items = 0;
  1047.     register Char *cp;
  1048.  
  1049.     if (cp = value(STRlistmax)) {
  1050.         while (*cp) {
  1051.         if (!Isdigit(*cp)) {
  1052.             max_items = 0;
  1053.             break;
  1054.         }
  1055.         max_items = max_items * 10 + *cp++ - '0';
  1056.         }
  1057.     }
  1058.  
  1059.     if ((max_items > 0) && (numitems > max_items) && list_max) {
  1060.         char    tc;
  1061.  
  1062.         xprintf("There are %d items, list them anyway? [n/y] ", numitems);
  1063.         flush();
  1064.         /* We should be in Rawmode here, so no \n to catch */
  1065.         (void) read(SHIN, &tc, 1);
  1066.         xprintf("%c\r\n", tc);    /* echo the char, do a newline */
  1067.         if ((tc != 'y') && (tc != 'Y'))
  1068.         goto done_list;
  1069.     }
  1070.     qsort((ptr_t) items, (size_t) numitems, sizeof(items[1]), 
  1071.           (int (*) __P((const void *, const void *))) fcompare);
  1072.  
  1073.     print_by_column(STRNULL, items, numitems, TRUE);
  1074.  
  1075. done_list:
  1076.     if (items != NULL)
  1077.         FREE_ITEMS(items, numitems);
  1078.     return (numitems);
  1079.     }
  1080.     else if (command == SPELL) {
  1081.     if (looking_for_lognames)
  1082.         copyn(word, STRtilde, 1);
  1083.     else if (looking_for_shellvar) {
  1084.         Char   *ptr = Strrchr(word, '$');
  1085.  
  1086.         *++ptr = '\0';    /* Delete after the dollar */
  1087.     }
  1088.     else if (looking_for_file)
  1089.         copyn(word, dir, max_word_length);    /* put back dir part */
  1090.     else
  1091.         word[0] = '\0';
  1092.     catn(word, extended_name, max_word_length);    /* add extended name */
  1093.     return (d);
  1094.     }
  1095.     else {
  1096.     xprintf("Bad tw_command\n");
  1097.     return (0);
  1098.     }
  1099. }
  1100.  
  1101.  
  1102.  
  1103. /* stuff for general command line hacking */
  1104.  
  1105. /*
  1106.  * Strip next directory from path; return ptr to next unstripped directory.
  1107.  */
  1108.  
  1109. #ifdef notdef
  1110. Char * extract_dir_from_path(path, dir)
  1111.     Char   *path, dir[];
  1112. {
  1113.     register Char *d = dir;
  1114.  
  1115.     while (*path && (*path == ' ' || *path == ':'))
  1116.     path++;
  1117.     while (*path && (*path != ' ' && *path != ':'))
  1118.     *(d++) = *(path++);
  1119.     while (*path && (*path == ' ' || *path == ':'))
  1120.     path++;
  1121.  
  1122.     ++dirctr;
  1123.     if (*dir == '.')
  1124.     (void) Strcpy(dirflag, STRdotsp);
  1125.     else {
  1126.     dirflag[0] = ' ';
  1127.     if (dirctr <= 9) {
  1128.         dirflag[1] = '0' + dirctr;
  1129.         dirflag[2] = '\0';
  1130.     }
  1131.     else {
  1132.         dirflag[1] = '0' + dirctr / 10;
  1133.         dirflag[2] = '0' + dirctr % 10;
  1134.         dirflag[3] = '\0';
  1135.     }
  1136.     }
  1137.     *(d++) = '/';
  1138.     *d = 0;
  1139.  
  1140.     return path;
  1141. }
  1142.  
  1143. #endif
  1144.  
  1145.  
  1146. static void
  1147. free_items(items, numitems)
  1148.     register Char **items;
  1149.     register int numitems;
  1150. {
  1151.     register int i;
  1152.  
  1153. /*     for (i = 0; items[i] != (Char *)NULL; i++) */
  1154.     for (i = 0; i < numitems; i++)
  1155.     xfree((ptr_t) items[i]);
  1156.     xfree((ptr_t) items);
  1157.     maxitems = 0;
  1158. }
  1159.  
  1160.  
  1161. /*
  1162.  * parse full path in file into 2 parts: directory and file names
  1163.  * Should leave final slash (/) at end of dir.
  1164.  */
  1165. static void
  1166. extract_dir_and_name(path, dir, name)
  1167.     Char   *path, *dir, *name;
  1168. {
  1169.     register Char *p;
  1170.  
  1171. #ifdef __MINT__
  1172.     p = Lastslash(path);
  1173. #else
  1174.     p = Strrchr(path, '/');
  1175. #endif
  1176.     if (p == NULL) {
  1177.     copyn(name, path, MAXNAMLEN);
  1178.     dir[0] = '\0';
  1179.     }
  1180.     else {
  1181.     p++;
  1182.     copyn(name, p, MAXNAMLEN);
  1183.     copyn(dir, path, p - path);
  1184.     }
  1185. }
  1186.  
  1187. static Char *
  1188. getentry(dir_fd, looking_for_lognames)
  1189.     DIR    *dir_fd;
  1190.     int     looking_for_lognames;
  1191. {
  1192.     register struct passwd *pw;
  1193.     static Char retname[MAXPATHLEN];
  1194.  
  1195.     register struct dirent *dirp;
  1196.  
  1197.     if (looking_for_lognames) {    /* Is it login names we want? */
  1198.     /*
  1199.      * We don't want to get interrupted inside getpwent()
  1200.      * because the yellow pages code is not interruptible,
  1201.      * and if we call endpwent() immediatetely after
  1202.      * (in pintr()) we may be freeing an invalid pointer
  1203.      */
  1204. #ifdef BSDSIGS
  1205.     sigmask_t omask = sigblock(sigmask(SIGINT));
  1206. #else
  1207.     (void) sighold(SIGINT);
  1208. #endif /* BSDSIGS */
  1209.     pw = getpwent();
  1210. #ifdef BSDSIGS
  1211.     (void) sigsetmask(omask);
  1212. #else
  1213.     (void) sigrelse(SIGINT);
  1214. #endif /* BSDSIGS */
  1215.  
  1216.     if (pw == NULL) {
  1217. #ifdef YPBUGS
  1218.         fix_yp_bugs();
  1219. #endif
  1220.         return (NULL);
  1221.     }
  1222.     (void) Strcpy(retname, str2short(pw->pw_name));
  1223.     return (retname);
  1224.     }
  1225.     else {            /* It's a dir entry we want */
  1226.     if (dirp = readdir(dir_fd)) {
  1227.         (void) Strcpy(retname, str2short(dirp->d_name));
  1228.         return (retname);
  1229.     }
  1230.     return (NULL);
  1231.     }
  1232. }
  1233.  
  1234. /*
  1235.  * expand "/$old1/$old2/old3/"
  1236.  * to "/value_of_old1/value_of_old2/old3/"
  1237.  */
  1238. static Char *
  1239. dollar(new, old)
  1240.     Char   *new, *old;
  1241. {
  1242.     Char   *var, *val, *p, save;
  1243.     int     space;
  1244.  
  1245.     for (space = FILSIZ, p = new; *old && space > 0;)
  1246.     if (*old != '$') {
  1247.         *p++ = *old++;
  1248.         space--;
  1249.     }
  1250.     else {
  1251.         struct varent *vp;
  1252.  
  1253.         /* found a variable, expand it */
  1254.         for (var = ++old; alnum(*old); old++);
  1255.         save = *old;
  1256.         *old = '\0';
  1257.         vp = adrof(var);
  1258.         val = (!vp) ? Getenv(var) : NULL;
  1259.         *old = save;
  1260.         /*
  1261.          * Don't expand array variables
  1262.          */
  1263.         if (vp) {
  1264.         if (!vp->vec[0] || vp->vec[1]) {
  1265.             *new = '\0';
  1266.             return (NULL);
  1267.         }
  1268.         else
  1269.             val = vp->vec[0];
  1270.         }
  1271.         else if (!val) {
  1272.         *new = '\0';
  1273.         return (NULL);
  1274.         }
  1275.         for (; space > 0 && *val; space--)
  1276.         *p++ = *val++;
  1277.     }
  1278.     *p = '\0';
  1279.     return (new);
  1280. }
  1281.  
  1282. /*
  1283.  * expand "old" file name with possible tilde usage
  1284.  *        ~person/mumble
  1285.  * expands to
  1286.  *        home_directory_of_person/mumble
  1287.  * into string "new".
  1288.  */
  1289.  
  1290. static Char *
  1291. tilde(new, old)
  1292.     Char   *new, *old;
  1293. {
  1294.     register Char *o, *p;
  1295.  
  1296.     if ((old[0] != '~') && (old[0] != '=')) {
  1297.     (void) Strcpy(new, old);
  1298.     return (new);
  1299.     }
  1300.  
  1301.     new[0] = '\0';
  1302. #ifdef __MINT__
  1303.     for (p = new, o = &old[1]; *o && !is_dirsep(*o); *p++ = *o++);
  1304. #else
  1305.     for (p = new, o = &old[1]; *o && *o != '/'; *p++ = *o++);
  1306. #endif
  1307.     *p = '\0';
  1308.  
  1309.     if (old[0] == '~') {
  1310.     if (gethdir(new))
  1311.         return (NULL);
  1312.     }
  1313.     else {            /* '=' stack expansion */
  1314.     if (!Isdigit(old[1]) && old[1] != '-')
  1315.         return (NULL);
  1316.     if (!getstakd(new, (old[1] == '-') ? -1 : old[1] - '0'))
  1317.         return (NULL);
  1318.     }
  1319.     (void) Strcat(new, o);
  1320.     return (new);
  1321. }
  1322.  
  1323. static  Char
  1324. filetype(dir, file)        /* symbology from 4.3 ls command */
  1325.     Char   *dir, *file;
  1326. {
  1327.     if (dir) {
  1328.     Char    path[512];
  1329.     char   *ptr;
  1330.     struct stat statb;
  1331.  
  1332.     (void) Strcpy(path, dir);
  1333.     catn(path, file, sizeof(path) / sizeof(Char));
  1334.  
  1335.     if (lstat(ptr = short2str(path), &statb) != -1)
  1336.         /* see above #define of lstat */
  1337.     {
  1338. #ifdef S_ISLNK
  1339.         if (S_ISLNK(statb.st_mode)) {    /* Symbolic link */
  1340.         if (adrof(STRlistlinks)) {
  1341.             if (stat(ptr, &statb) == -1)
  1342.             return ('&');
  1343.             else if (S_ISDIR(statb.st_mode))
  1344.             return ('>');
  1345.             else
  1346.             return ('@');
  1347.         }
  1348.         else
  1349.             return ('@');
  1350.         }
  1351. #endif
  1352. #ifdef S_ISSOCK
  1353.         if (S_ISSOCK(statb.st_mode))    /* Socket */
  1354.         return ('=');
  1355. #endif
  1356. #ifdef S_ISFIFO
  1357.         if (S_ISFIFO(statb.st_mode)) /* Named Pipe */
  1358.         return ('|');
  1359. #endif
  1360. #ifdef S_ISHIDDEN
  1361.         if (S_ISHIDDEN(statb.st_mode)) /* Hidden Directory [aix] */
  1362.         return ('+');
  1363. #endif
  1364. #ifdef S_ISCDF    
  1365.         if (S_ISCDF(statb.st_mode))    /* Context Dependent Files [hpux] */
  1366.         return ('+');
  1367. #endif 
  1368. #ifdef S_ISNWK
  1369.         if (S_ISNWK(statb.st_mode)) /* Network Special [hpux] */
  1370.         return (':');
  1371. #endif
  1372.         if (S_ISCHR(statb.st_mode))    /* char device */
  1373.         return ('%');
  1374.         if (S_ISBLK(statb.st_mode))    /* block device */
  1375.         return ('#');
  1376.         if (S_ISDIR(statb.st_mode))    /* normal Directory */
  1377. #ifdef __MINT__
  1378.         if (is_set(STRdosslashes))
  1379.             return ('\\');
  1380.         else
  1381. #endif
  1382.         return ('/');
  1383.         if (statb.st_mode & 0111)
  1384.         return ('*');
  1385.     }
  1386.     }
  1387.     return (' ');
  1388. }
  1389.  
  1390. static int
  1391. isadirectory(dir, file)        /* return 1 if dir/file is a directory */
  1392.     Char   *dir, *file;        /* uses stat rather than lstat to get dest. */
  1393. {
  1394.     if (dir) {
  1395.     Char    path[MAXPATHLEN];
  1396.     struct stat statb;
  1397.  
  1398.     (void) Strcpy(path, dir);
  1399.     catn(path, file, sizeof(path) / sizeof(Char));
  1400.     if (stat(short2str(path), &statb) >= 0) {    /* resolve through
  1401.                              * symlink */
  1402. #ifdef S_ISSOCK
  1403.         if (S_ISSOCK(statb.st_mode))    /* Socket */
  1404.         return 0;
  1405. #endif
  1406. #ifdef S_ISFIFO
  1407.         if (S_ISFIFO(statb.st_mode))    /* Named Pipe */
  1408.         return 0;
  1409. #endif
  1410.         if (S_ISDIR(statb.st_mode))    /* normal Directory */
  1411.         return 1;
  1412.     }
  1413.     }
  1414.     return 0;
  1415. }
  1416.  
  1417. /*
  1418.  * Print sorted down columns
  1419.  */
  1420. void
  1421. print_by_column(dir, items, count, no_file_suffix)
  1422.     register Char *dir, *items[];
  1423.     int     count, no_file_suffix;
  1424. {
  1425.     register int i, r, c, w, maxwidth = 0, columns, rows;
  1426.     extern int Tty_raw_mode;
  1427.  
  1428.     lbuffed = 0;        /* turn off line buffering */
  1429.  
  1430.     for (i = 0; i < count; i++)    /* find widest string */
  1431.     maxwidth = max(maxwidth, Strlen(items[i]));
  1432.  
  1433.     maxwidth += no_file_suffix ? 1 : 2;    /* for the file tag and space */
  1434.     columns = (TermH + 1) / maxwidth;    /* PWP: terminal size change */
  1435.     if (!columns)
  1436.     columns = 1;
  1437.     rows = (count + (columns - 1)) / columns;
  1438.  
  1439.     for (r = 0; r < rows; r++) {
  1440.     for (c = 0; c < columns; c++) {
  1441.         i = c * rows + r;
  1442.  
  1443.         if (i < count) {
  1444.         w = Strlen(items[i]);
  1445.  
  1446.         if (no_file_suffix) {
  1447.             /* Print the command name */
  1448.             xprintf("%s", short2str(items[i]));
  1449.         }
  1450.         else {
  1451.             /* Print filename followed by '/' or '*' or ' ' */
  1452.             xprintf("%s%c", short2str(items[i]),
  1453.                 filetype(dir, items[i]));
  1454.             w++;
  1455.         }
  1456.  
  1457.         if (c < (columns - 1))    /* Not last column? */
  1458.             for (; w < maxwidth; w++)
  1459.             xputchar(' ');
  1460.         }
  1461.     }
  1462.     if (Tty_raw_mode)
  1463.         xputchar('\r');
  1464.     xputchar('\n');
  1465.     }
  1466.  
  1467.     lbuffed = 1;        /* turn back on line buffering */
  1468.     flush();
  1469. }
  1470.  
  1471.  
  1472. int
  1473. StrQcmp(str1, str2)
  1474.     register Char *str1, *str2;
  1475. {
  1476.     for (; *str1 && (*str1 & TRIM) == (*str2 & TRIM); str1++, str2++);
  1477.     /*
  1478.      * The following case analysis is necessary so that characters which look
  1479.      * negative collate low against normal characters but high against the
  1480.      * end-of-string NUL.
  1481.      */
  1482.     if (*str1 == '\0' && *str2 == '\0')
  1483.     return (0);
  1484.     else if (*str1 == '\0')
  1485.     return (-1);
  1486.     else if (*str2 == '\0')
  1487.     return (1);
  1488.     else
  1489.     return ((*str1 & TRIM) - (*str2 & TRIM));
  1490. }
  1491.  
  1492. /*
  1493.  * For qsort()
  1494.  */
  1495. int
  1496. fcompare(file1, file2)
  1497.     Char  **file1, **file2;
  1498. {
  1499. #if defined(NLS) && !defined(NOSTRCOLL)
  1500.     char    buf[2048];
  1501.  
  1502.     (void) strcpy(buf, short2str(*file1));
  1503.     return ((int) strcoll(buf, short2str(*file2)));
  1504. #else
  1505.     return (StrQcmp(*file1, *file2));
  1506. #endif
  1507. }
  1508.  
  1509. /*
  1510.  * Concatenate src onto tail of des.
  1511.  * Des is a string whose maximum length is count.
  1512.  * Always null terminate.
  1513.  */
  1514.  
  1515. void
  1516. catn(des, src, count)
  1517.     register Char *des, *src;
  1518.     register count;
  1519. {
  1520.     while (--count >= 0 && *des)
  1521.     des++;
  1522.     while (--count >= 0)
  1523.     if ((*des++ = *src++) == 0)
  1524.         return;
  1525.     *des = '\0';
  1526. }
  1527.  
  1528. /*
  1529.  * like strncpy but always leave room for trailing \0
  1530.  * and always null terminate.
  1531.  */
  1532. void
  1533. copyn(des, src, count)
  1534.     register Char *des, *src;
  1535.     register count;
  1536. {
  1537.     while (--count >= 0)
  1538.     if ((*des++ = *src++) == 0)
  1539.         return;
  1540.     *des = '\0';
  1541. }
  1542.  
  1543. static void
  1544. tw_get_comm_list()
  1545. {                /* stolen from sh.exec.c dohash() */
  1546.     register DIR *dirp;
  1547.     register struct dirent *dp;
  1548.     register Char *dir;
  1549.     register Char **pv;
  1550.     struct varent *v = adrof(STRpath);
  1551.  
  1552.     relatives_in_path = 0;    /* set to false until we know better */
  1553.     tw_clear_comm_list();
  1554.     if (v == 0)            /* if no path */
  1555.     return;
  1556.  
  1557.     if (adrof(STRrecognize_only_executables)) {
  1558.     for (pv = v->vec; *pv; pv++) {
  1559. #ifdef __MINT__
  1560.         if (pv[0][0] != '/' && pv[0][0] != '\\') {
  1561. #else
  1562.         if (pv[0][0] != '/') {
  1563. #endif
  1564.         relatives_in_path = 1;
  1565.         continue;
  1566.         }
  1567.         dirp = opendir(short2str(*pv));
  1568.         if (dirp == NULL)
  1569.         continue;
  1570.  
  1571.         dir = Strspl(*pv, STRslash);
  1572.         while ((dp = readdir(dirp)) != NULL) {
  1573.         /* the call to executable() may make this a bit slow */
  1574.         if (dp->d_ino != 0 &&
  1575.             executable(dir, str2short(dp->d_name), 0))
  1576.             tw_add_comm_name(str2short(dp->d_name));
  1577.         }
  1578.         (void) closedir(dirp);
  1579.         xfree((ptr_t) dir);
  1580.     }
  1581.     }
  1582.     else {
  1583.     for (pv = v->vec; *pv; pv++) {
  1584. #ifdef __MINT__
  1585.         if (pv[0][0] != '/' && pv[0][0] != '\\') {
  1586. #else
  1587.         if (pv[0][0] != '/') {
  1588. #endif
  1589.         relatives_in_path = 1;
  1590.         continue;
  1591.         }
  1592.         dirp = opendir(short2str(*pv));
  1593.         if (dirp == NULL)
  1594.         continue;
  1595.  
  1596.         while ((dp = readdir(dirp)) != NULL) {
  1597.         if (dp->d_ino != 0)
  1598.             tw_add_comm_name(str2short(dp->d_name));
  1599.         }
  1600.         (void) closedir(dirp);
  1601.     }
  1602.     }
  1603. }
  1604.